#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "hardware/spi.h"

#include "wizchip_conf.h"
#include "bsp_spi.h"
#include "dhcp.h"   // Use dhcp
#include "socket.h" // Use socket

#define SOCKET_ID 0                      // Socket number
#define ETHERNET_BUF_MAX_SIZE (1024 * 2) // Send and receive cache size
#define DHCP_RETRY_COUNT 5               // DHCP retry times

/**
 * @brief   Timer callback processing function, used for dhcp timing processing
 * @param   repeating :Timer structure
 * @return  bool
 */
bool repeating_timer_callback(struct repeating_timer *t);

/**
 * @brief   Initialization of chip network information
 * @param   conf_info :Static configuration information
 * @return  none
 */
void network_init(wiz_NetInfo *conf_info);

void ISR(void);
void loopback_tcps(uint8_t sn, uint8_t* buf, uint16_t port);
void gpio_callback(uint gpio, uint32_t events)
{
    ISR();
}


/* Network information to be configured. */
wiz_NetInfo net_info = {
    .mac = {0x00, 0x08, 0xdc, 0x11, 0x22, 0x33}, // Configured MAC address
    .ip = {192, 168, 1, 10},                     // Configured IP address
    .sn = {255, 255, 255, 0},                    // Configured subnet mask
    .gw = {192, 168, 1, 1},                      // Configured gateway
    .dns = {8, 8, 8, 8},                         // Configured domain address
    .dhcp = NETINFO_DHCP};                       // Configured dhcp model,NETINFO_DHCP:use dhcp; NETINFO_STATIC: use static ip.

static uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {
    0,
}; // Send and receive cachestatic

enum SN_STATUS{
     closed_status = 0,
     ready_status,
     connected_status,
};
static uint8_t breakout_flag = 0; // Define the DHCP acquisition flag
static uint8_t I_STATUS[_WIZCHIP_SOCK_NUM_];
static uint8_t ch_status[_WIZCHIP_SOCK_NUM_]={0};
static uint16_t loadport = 8080;

int main()
{
    struct repeating_timer timer; // Define the timer structure
    wiz_NetInfo get_info;         // Stores the read configuration information
    wiz_PhyConf phy_conf, get_conf;

    /* MCU init */
    stdio_init_all();              // Initialize the main control peripheral
    wizchip_initialize();          // Initialize the chip interface
    wizchip_setnetinfo(&net_info); // Configure once first

    /*dhcp init*/
    DHCP_init(SOCKET_ID, ethernet_buf);                                   // DHCP initialization
    add_repeating_timer_ms(1000, repeating_timer_callback, NULL, &timer); // Add DHCP 1s Tick Timer handler

    printf("wiznet chip interrupt method example.\r\n");
    network_init(&net_info);              // Configuring Network Information
    print_network_information(&get_info); // Read back the configuration information and print it

    gpio_set_irq_enabled_with_callback(2, GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
  

   


    while (true)
    {
        sleep_ms(500);
		loopback_tcps(SOCKET_ID, ethernet_buf, loadport);	
    }
}

void network_init(wiz_NetInfo *conf_info)
{
    int count = 0;
    uint8_t dhcp_retry = 0;

    if (conf_info->dhcp == NETINFO_DHCP)
    {
        while (true)
        {
            switch (DHCP_run()) // Do the DHCP client
            {
            case DHCP_IP_LEASED: // DHCP resolves the domain name successfully
            {
                if (breakout_flag == 0)
                {
                    printf("DHCP success\r\n");
                    getIPfromDHCP((*conf_info).ip);
                    getGWfromDHCP((*conf_info).gw);
                    getSNfromDHCP((*conf_info).sn);
                    getDNSfromDHCP((*conf_info).dns);
                    wizchip_setnetinfo(conf_info); // Configuring Network Information
                    close(SOCKET_ID);              // After dhcp close the socket, avoid errors in later use
                    breakout_flag = 1;
                }
                break;
            }
            case DHCP_FAILED:
            {
                printf(" DHCP failed \r\n");
                count++;
                if (count <= DHCP_RETRY_COUNT) // If the number of times is less than or equal to the maximum number of times, try again
                {
                    printf("DHCP timeout occurred and retry %d \r\n", count);
                }
                else if (count > DHCP_RETRY_COUNT) // If the number of times is greater than DHCP fails
                {
                    breakout_flag = 1; // if DHCP fail, use the static
                    DHCP_stop();       // Stop processing DHCP protocol
                    conf_info->dhcp = NETINFO_STATIC;
                    wizchip_setnetinfo(conf_info); // Configuring Network Information
                    break;
                }
                break;
            }
            }
            if (breakout_flag)
            {
                printf("config succ\r\n");
                break;
            }
        }
    }
    else
    {
        wizchip_setnetinfo(conf_info); // Configuring Network Information
    }
}

bool repeating_timer_callback(struct repeating_timer *t)
{
    DHCP_time_handler(); // DHCP 1s Tick Timer handler
    return true;
}


void ISR(void)
{
    uint8_t IR_val = 0, SIR_val = 0;
    uint8_t tmp,sn;


    setIMR(0x00);
    IR_val = getIR();

#if (_WIZCHIP_ == W5500)
    setSIMR(0x00);
    SIR_val = getSIR();
#elif (_WIZCHIP_ == W5100S)
    uint8_t IR2_val=0;
    setIMR2(0);
    IR2_val = getIR2();
#endif

   if(IR_val > 0)
   {
        if(IR_val & IR_CONFLICT)
        {
            printf("IP conflict : %.2x\r\n", IR_val);
        }
        if(IR_val & IR_PPPoE)
        {
            printf("PPPoE closed : %.2x\r\n", IR_val);
        }
        if(IR_val&IR_UNREACH)
        {
            printf("Destination unreached : %.2x\r\n", IR_val);
        }
#if (_WIZCHIP_ == W5100S)

        if(IR2_val & 0x01)
        {
            printf("Magic packet : %.2x\r\n", IR_val);
        }

#elif (_WIZCHIP_ == W5500)
        if(IR_val & IR_MP)
        {
            printf("Magic packet : %.2x\r\n", IR_val);
        }

#endif

        setIR(IR_val); // clear interrupt
   }

   for(sn =0;sn<_WIZCHIP_SOCK_NUM_;sn++)
   {
        tmp = 0;
        if(IR_val & IR_SOCK(sn))
        {
            tmp = getSn_IR(sn);
            I_STATUS[sn] |= tmp;
            tmp &= 0x0f;
            setSn_IR(sn, tmp);
        }
   }
    
    #if (_WIZCHIP_ == W5500)
    setIMR(0xf0);
    setSIMR(0xff);
    #elif (_WIZCHIP_ == W5100S)
    setIMR(0xff);
    setIMR2(0x01);
    #endif
}


void loopback_tcps(uint8_t sn, uint8_t* buf, uint16_t port)
{
    uint16_t len = 0;
    uint8_t tmp = 0;

    if(I_STATUS[sn] == SOCK_CLOSED)
    {                                                                   printf("sock_closed\r\n");
        if(!ch_status[sn])
        {
            ch_status[sn]=ready_status;
            if(socket(sn, Sn_MR_TCP, port, 0x00) != sn)
            {
                ch_status[sn]=closed_status;
            }
            else
            {
                listen(sn);
            }
        }
    }
    if(I_STATUS[sn]&Sn_IR_CON)
    {                                                           printf("sock_ir_con\r\n");
        ch_status[sn]=connected_status;
        I_STATUS[sn]&= ~(0x01);
    }
    if(I_STATUS[sn]&Sn_IR_DISCON)
    {                                                               printf("sock_ir_discon\r\n");
        if((getSn_RX_RSR(sn))> 0)
        {
            if(len>ETHERNET_BUF_MAX_SIZE)
            {
                len=ETHERNET_BUF_MAX_SIZE;
            }
            recv(sn, buf, len);
            buf[len]=0x00;
            printf("%s\r\n", buf);
        }
        disconnect(sn);
        ch_status[sn] = closed_status;
        I_STATUS[sn]&= ~(0x02);
    }
    if(I_STATUS[sn]&Sn_IR_RECV)
    {                                                   printf("sn_ir_recv\r\n");
        setIMR(0x00);
        setIMR2(0x00);
        tmp = I_STATUS[sn];
        I_STATUS[sn]&=~(0x04);

        setIMR(0xff);
        setIMR2(0x01);
        if(tmp & Sn_IR_RECV)
        {
            if((len = getSn_RX_RSR(sn))>0)
            {
                if(len > ETHERNET_BUF_MAX_SIZE)
                {
                    len=ETHERNET_BUF_MAX_SIZE;
                }
                len = recv(sn, buf, len);
                buf[len] = 0x00;
                printf("%s\r\n",buf);
                send(sn, buf, len);
                tmp = I_STATUS[sn];
            }
        }
    }
    if(I_STATUS[sn]&Sn_IR_SENDOK)
    {                                                       printf("sn_ir_sendok\r\n");
        I_STATUS[sn] &=~(0x10);
    }
}